//
//  RCHttpRequestSerialization.m
//  RCNetRequestProject
//
//  Created by kabu on 2017/7/18.
//  Copyright © 2017年 RC. All rights reserved.
//

#import "RCHttpRequestSerialization.h"
#import <TargetConditionals.h>
#import <UIKit/UIKit.h>
#import <MobileCoreServices/MobileCoreServices.h>
#define RCNetFormBoundary  @"RCNetFormBoundaryftnnT7s3iF7wV5q6"
#define RCUPLoadFormNewLine [@"\r\n" dataUsingEncoding:NSUTF8StringEncoding]
/**
 特殊字符串过滤--------摘自AFNetWorking
 
 @param string 待过滤字符串
 @return 过滤特殊字符后的字符串
 */
NSString * RCNetPercentEscapedStringFromString(NSString *string) {
    static NSString * const kRCNetCharactersGeneralDelimitersToEncode = @":#[]@"; // does not include "?" or "/" due to RFC 3986 - Section 3.4
    static NSString * const kRCNetCharactersSubDelimitersToEncode = @"!$&'()*+,;=";
    
    NSMutableCharacterSet * allowedCharacterSet = [[NSCharacterSet URLQueryAllowedCharacterSet] mutableCopy];
    [allowedCharacterSet removeCharactersInString:[kRCNetCharactersGeneralDelimitersToEncode stringByAppendingString:kRCNetCharactersSubDelimitersToEncode]];
    
    
    static NSUInteger const batchSize = 50;
    
    NSUInteger index = 0;
    NSMutableString *escaped = @"".mutableCopy;
    
    while (index < string.length) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wgnu"
        NSUInteger length = MIN(string.length - index, batchSize);
#pragma GCC diagnostic pop
        NSRange range = NSMakeRange(index, length);
        
        // To avoid breaking up character sequences such as 👴🏻👮🏽
        range = [string rangeOfComposedCharacterSequencesForRange:range];
        
        NSString *substring = [string substringWithRange:range];
        NSString *encoded = [substring stringByAddingPercentEncodingWithAllowedCharacters:allowedCharacterSet];
        [escaped appendString:encoded];
        
        index += range.length;
    }
    
    return escaped;
}
@interface RCNetQueryStringPair : NSObject
@property (readwrite, nonatomic, strong) id field;
@property (readwrite, nonatomic, strong) id value;

@end
@implementation RCNetQueryStringPair

- (instancetype)initWithField:(id)aField value:(id)aValue{
    self = [super init];
    if (self) {
        self.field=aField;
        self.value=aValue;
    }
    return  self;
}

/**
 将对象的值进行URL编码并且拼接
 
 @return QueryString
 */
- (NSString *)URLEncodedStringValue {
    if (!self.value || [self.value isEqual:[NSNull null]]) {
        return RCNetPercentEscapedStringFromString([self.field description]);
    } else {
        return [NSString stringWithFormat:@"%@=%@", RCNetPercentEscapedStringFromString([self.field description]), RCNetPercentEscapedStringFromString([self.value description])];
    }
}

@end

#pragma mark - 对于字典的转换和拼接 ------摘自AFNetworking

FOUNDATION_EXPORT NSArray * RCNetQueryStringPairsFromDictionary(NSDictionary *dictionary);
FOUNDATION_EXPORT NSArray * RCNetQueryStringPairsFromKeyAndValue(NSString *key, id value);

/**
 将字典转换为QueryString
 
 @param parameters 字典/数组
 @return QueryString
 */
NSString * RCNetQueryStringFromParameters(NSDictionary *parameters) {
    NSMutableArray *mutablePairs = [NSMutableArray array];
    for (RCNetQueryStringPair *pair in RCNetQueryStringPairsFromDictionary(parameters)) {
        [mutablePairs addObject:[pair URLEncodedStringValue]];
    }
    
    return [mutablePairs componentsJoinedByString:@"&"];
}

NSArray * RCNetQueryStringPairsFromDictionary(NSDictionary *dictionary) {
    return RCNetQueryStringPairsFromKeyAndValue(nil, dictionary);
}

/**
 将字典转换为RCNetQueryStringPair对象数组-----使用递归循环将字典对象转换为RCNetQueryStringPair
 
 @param key 字典的key
 @param value 字典的vlaue
 @return 对象数组
 */
NSArray * RCNetQueryStringPairsFromKeyAndValue(NSString *key, id value) {
    NSMutableArray *mutableQueryStringComponents = [NSMutableArray array];
    
    NSSortDescriptor *sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:@"description" ascending:YES selector:@selector(compare:)];
    
    if ([value isKindOfClass:[NSDictionary class]]) {
        NSDictionary *dictionary = value;
        // Sort dictionary keys to ensure consistent ordering in query string, which is important when deserializing potentially ambiguous sequences, such as an array of dictionaries
        for (id nestedKey in [dictionary.allKeys sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
            id nestedValue = dictionary[nestedKey];
            if (nestedValue) {
                [mutableQueryStringComponents addObjectsFromArray:RCNetQueryStringPairsFromKeyAndValue((key ? [NSString stringWithFormat:@"%@[%@]", key, nestedKey] : nestedKey), nestedValue)];
            }
        }
    } else if ([value isKindOfClass:[NSArray class]]) {
        NSArray *array = value;
        for (id nestedValue in array) {
            [mutableQueryStringComponents addObjectsFromArray:RCNetQueryStringPairsFromKeyAndValue([NSString stringWithFormat:@"%@[]", key], nestedValue)];
        }
    } else if ([value isKindOfClass:[NSSet class]]) {
        NSSet *set = value;
        for (id obj in [set sortedArrayUsingDescriptors:@[ sortDescriptor ]]) {
            [mutableQueryStringComponents addObjectsFromArray:RCNetQueryStringPairsFromKeyAndValue(key, obj)];
        }
    } else {
        [mutableQueryStringComponents addObject:[[RCNetQueryStringPair alloc] initWithField:key value:value]];
    }
    
    return mutableQueryStringComponents;
}
static inline NSString * RCContentTypeForPathExtension(NSString *extension) {
    NSString *UTI = (__bridge_transfer NSString *)UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, (__bridge CFStringRef)extension, NULL);
    NSString *contentType = (__bridge_transfer NSString *)UTTypeCopyPreferredTagWithClass((__bridge CFStringRef)UTI, kUTTagClassMIMEType);
    if (!contentType) {
        return @"application/octet-stream";
    } else {
        return contentType;
    }
}
static inline NSMutableData *RCFormDataWithParams(id parameters,NSMutableData *data){
    if ([parameters isKindOfClass:[NSDictionary class]]) {
        NSDictionary *dictPar=parameters;
        for (NSString *key in dictPar.allKeys) {
            id valueS=[dictPar objectForKey:key];
            
            // 设置边界
            [data appendData:[[NSString stringWithFormat:@"--%@",RCNetFormBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
            [data appendData:RCUPLoadFormNewLine];
            //    *****************非文件参数**************************
            // 内容设置 ， 设置传入的类型（名称）
            [data appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"",key] dataUsingEncoding:NSUTF8StringEncoding]];
            [data appendData:RCUPLoadFormNewLine];
            [data appendData:RCUPLoadFormNewLine];
            
            // 传入的名称username = lxl
            if ([valueS isKindOfClass:[NSString class]]) {
                [data appendData:[[NSString stringWithFormat:@"%@",valueS] dataUsingEncoding:NSUTF8StringEncoding]];
            }else if([valueS isKindOfClass:[NSData class]]){
                [data appendData:valueS];
            }else if([valueS isKindOfClass:[NSNumber class]]){
                [data appendData:[[NSString stringWithFormat:@"%@",valueS] dataUsingEncoding:NSUTF8StringEncoding]];
            }
            
            [data appendData:RCUPLoadFormNewLine];
        }
    }else if([parameters isKindOfClass:[NSArray class]]){
        NSArray *array=parameters;
        for (id items in array) {
            if ([items isKindOfClass:[NSDictionary class]]) {
                data = RCFormDataWithParams(items, data);
            }else if([items isKindOfClass:[NSArray class]]){
                data = RCFormDataWithParams(items, data);
            }
        }
    }
    return data;
}
@interface RCFormData ()

@end
@implementation RCFormData
+(NSMutableData*)formData:(NSData *)data parameters:(id)parameters mimeType:(NSString *)mimeType fileName:(NSString *)fileName name:(NSString *)name{
    NSMutableData *formData=[self data:data parameters:parameters mimeType:mimeType fileName:fileName name:name];
    return formData;
}
+ (NSMutableData*)data:(NSData *)fdata parameters:(id)parameters mimeType:(NSString *)mimeType fileName:(NSString *)fileName name:(NSString *)name{
    // 给请求体加入固定格式数据
    NSMutableData *data = [NSMutableData data];
    /*******************设置文件参数***********************/
    // 设置边界 注：必须和请求头数据设置的边界 一样， 前面多两个“-”；（字符串 转 data 数据）
    [data appendData:[[NSString stringWithFormat:@"--%@",RCNetFormBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
    [data appendData:RCUPLoadFormNewLine];
    // 设置传入数据的基本属性， 包括有 传入方式 data ，传入的类型（名称） ，传入的文件名， 。
    [data appendData:[[NSString stringWithFormat:@"Content-Disposition: form-data; name=\"%@\"; filename=\"%@\"",name,fileName] dataUsingEncoding:NSUTF8StringEncoding]];
    [data appendData:RCUPLoadFormNewLine];
    
    // 设置 内容的类型  “文件类型/扩展名” MIME中的
    [data appendData:[[NSString stringWithFormat:@"Content-Type: %@",mimeType]dataUsingEncoding:NSUTF8StringEncoding]];
    [data appendData:RCUPLoadFormNewLine];
    [data appendData:RCUPLoadFormNewLine];
    // 加入数据内容
    [data appendData:fdata];
//    [data appendData:[@"测试文件数据" dataUsingEncoding:NSUTF8StringEncoding]];
    [data appendData:RCUPLoadFormNewLine];
    
    if (parameters) {
        data = RCFormDataWithParams(parameters, data);
    }
    
    // 退出边界
    [data appendData:[[NSString stringWithFormat:@"--%@--",RCNetFormBoundary] dataUsingEncoding:NSUTF8StringEncoding]];
//    NSString *log=[[NSString alloc]initWithData:data encoding:NSUTF8StringEncoding];
//    NSLog(@"form log \n %@",log);
    return data;
}
@end
@interface RCHttpRequestSerialization()
@property (readwrite, nonatomic, strong) NSMutableDictionary *mutableHTTPRequestHeaders;
@property (nonatomic, strong) NSSet <NSString *> *HTTPMethodsEncodingParametersInURI;

@end
@implementation RCHttpRequestSerialization

-(instancetype)init{
    self = [super init];
    if (self) {
        self.timeoutInterval=20;
        self.cachePolicy=NSURLRequestUseProtocolCachePolicy;
        self.mutableHTTPRequestHeaders=[[NSMutableDictionary alloc]init];
        self.stringEncoding=NSUTF8StringEncoding;
        NSMutableArray *acceptLanguagesComponents = [NSMutableArray array];
        [[NSLocale preferredLanguages] enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
            float q = 1.0f - (idx * 0.1f);
            [acceptLanguagesComponents addObject:[NSString stringWithFormat:@"%@;q=%0.1g", obj, q]];
            *stop = q <= 0.5f;
        }];
        [self setValue:[acceptLanguagesComponents componentsJoinedByString:@", "] forHttpHeader:@"Accept-Language"];
        NSString *userAgent = nil;
        
        // User-Agent Header; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.43
        userAgent = [NSString stringWithFormat:@"%@/%@ (%@; iOS %@; Scale/%0.2f)", [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleExecutableKey] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleIdentifierKey], [[NSBundle mainBundle] infoDictionary][@"CFBundleShortVersionString"] ?: [[NSBundle mainBundle] infoDictionary][(__bridge NSString *)kCFBundleVersionKey], [[UIDevice currentDevice] model], [[UIDevice currentDevice] systemVersion], [[UIScreen mainScreen] scale]];
        if (userAgent) {
            if (![userAgent canBeConvertedToEncoding:NSASCIIStringEncoding]) {
                NSMutableString *mutableUserAgent = [userAgent mutableCopy];
                if (CFStringTransform((__bridge CFMutableStringRef)(mutableUserAgent), NULL, (__bridge CFStringRef)@"Any-Latin; Latin-ASCII; [:^ASCII:] Remove", false)) {
                    userAgent = mutableUserAgent;
                }
            }
            [self setValue:userAgent forHttpHeader:@"User-Agent"];
        }
        // HTTP Method Definitions; see http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html
        self.HTTPMethodsEncodingParametersInURI = [NSSet setWithObjects:@"GET", @"HEAD", @"DELETE", nil];
        
    }
    return self;
}
+(instancetype)serializer{
    return [[self alloc] init];
}
-(void)setValue:(NSString *)value forHttpHeader:(NSString *)field{
    [self.mutableHTTPRequestHeaders setValue:value forKey:field];
}

-(NSString*)valueForHttpHeader:(NSString*)field{
    return [self.mutableHTTPRequestHeaders valueForKey:field];
}
-(NSDictionary<NSString *, NSString *>*)httpRequestHeaders{
    return [self.mutableHTTPRequestHeaders copy];
}
-(NSString*)queryStringWithParam:(id)params{
    NSString *queryStr=RCNetQueryStringFromParameters(params);
    NSLog(@"query string %@ ",queryStr);
    return queryStr;
}


/** 指定全路径文件的mimeType */
//- (NSString *)mimeTypeWithFilePath:(NSString *)filePath
//{
//    // 1. 判断文件是否存在
//    if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
//        return nil;
//    }
//    
//    // 2. 使用HTTP HEAD方法获取上传文件信息
//    NSURL *url = [NSURL fileURLWithPath:filePath];
//    NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:url];
//    
//    // 3. 调用同步方法获取文件的MimeType
//    NSURLResponse *response = nil;
//    [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:NULL];
//    
//    return response.MIMEType;
//}

- (NSMutableURLRequest *)requestWithMethod:(NSString *)method URLString:(NSString *)URLString parameters:(id)parameters error:(NSError * __autoreleasing *)error{
//    NSParameterAssert(method);
    NSParameterAssert(URLString);
    
    NSURL *url = [NSURL URLWithString:URLString];
    
    NSParameterAssert(url);
    NSMutableURLRequest *sourceReq = [NSMutableURLRequest requestWithURL:url cachePolicy:self.cachePolicy timeoutInterval:self.timeoutInterval];
    if (method) {
        sourceReq.HTTPMethod = method;
    }
    NSMutableURLRequest *mutableRequest = [self requestBySerialization:sourceReq params:parameters error:error];
    if (*error) {
        return sourceReq;
    }
    
    return mutableRequest;
}
- (NSMutableURLRequest *)requestWithFormData:(NSData*)data urlString:(NSString*)URLString parameters:(id)parameters fileName:(NSString*)fileName name:(NSString*)name mimeType:(NSString*)mimeType{
    NSURL *url = [NSURL URLWithString:URLString];
    NSMutableURLRequest *sourceReq = [NSMutableURLRequest requestWithURL:url];
    // 设置请求方式 post
    sourceReq.HTTPMethod = @"POST";
    [sourceReq setValue:[NSString stringWithFormat:@"multipart/form-data; boundary=%@",RCNetFormBoundary] forHTTPHeaderField:@"Content-Type"];
    NSData *afromData=[RCFormData formData:data parameters:parameters mimeType:mimeType fileName:fileName name:name];
    sourceReq.HTTPBody =afromData;
    //设置请求头总数据长度
    [sourceReq setValue:[NSString stringWithFormat:@"%lu",(unsigned long)afromData.length] forHTTPHeaderField:@"Content-Length"];
    
    return sourceReq;
}
#pragma mark RCURLRequestSerialization
-(NSMutableURLRequest*)requestBySerialization:(NSMutableURLRequest *)request params:(id)aParams error:(NSError *__autoreleasing *)error{
    //requestHeader处理
    [self.mutableHTTPRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
        if (![request valueForHTTPHeaderField:field]) {
            [request setValue:value forHTTPHeaderField:field];
        }
    }];
    //query String 处理
    NSString *query = [self queryStringWithParam:aParams];
    if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
        if (query && query.length > 0) {
            request.URL = [NSURL URLWithString:[[request.URL absoluteString] stringByAppendingFormat:request.URL.query ? @"&%@" : @"?%@", query]];
        }
    } else {
        // #2864: an empty string is a valid x-www-form-urlencoded payload
        if (!query) {
            query = @"";
        }
        if (![request valueForHTTPHeaderField:@"Content-Type"]) {
            [request setValue:@"application/x-www-form-urlencoded" forHTTPHeaderField:@"Content-Type"];
        }
        [request setHTTPBody:[query dataUsingEncoding:self.stringEncoding]];
    }
    
    return request;
}
@end

@implementation RCJsonRequestSerialization
+ (instancetype)serializer {
    return [self serializerWithWritingOptions:(NSJSONWritingOptions)0];
}

+ (instancetype)serializerWithWritingOptions:(NSJSONWritingOptions)writingOptions
{
    RCJsonRequestSerialization *serializer = [[self alloc] init];
    serializer.writingOptions = writingOptions;
    
    return serializer;
}
-(NSMutableURLRequest*)requestBySerialization:(NSMutableURLRequest *)request params:(id)aParams error:(NSError *__autoreleasing *)error{
    NSParameterAssert(request);
    
    if ([self.HTTPMethodsEncodingParametersInURI containsObject:[[request HTTPMethod] uppercaseString]]) {
        return [super requestBySerialization:request params:aParams error:error];
    }
    
    NSMutableURLRequest *mutableRequest = [request mutableCopy];
    
    [self.httpRequestHeaders enumerateKeysAndObjectsUsingBlock:^(id field, id value, BOOL * __unused stop) {
        if (![request valueForHTTPHeaderField:field]) {
            [mutableRequest setValue:value forHTTPHeaderField:field];
        }
    }];
    
    if (aParams) {
        if (![mutableRequest valueForHTTPHeaderField:@"Content-Type"]) {
            [mutableRequest setValue:@"application/json" forHTTPHeaderField:@"Content-Type"];
        }
        NSData *jsonData=[NSJSONSerialization dataWithJSONObject:aParams options:self.writingOptions error:error];
        [mutableRequest setHTTPBody:jsonData];
    }
    
    return mutableRequest;
}



@end
